﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Xrm.Sdk;
using XrmLibrary.Exceptions;
using XrmLibrary.Helpers;

namespace XrmLibrary.MsCrm4Adapter.Extensions
{
    public static class PropertyExtensions
    {
        public static KeyValuePair<String, Object> ToCrm2011Attribute(this Property property)
        {
            return new KeyValuePair<String, Object>(property.Name, GetCrm2011AttributeValue(property));
        }

        private static Object GetCrm2011AttributeValue(this Property property)
        {
            Object attributeValue = null;
            TypeSwitch.Do(property,
                          TypeSwitch.Case<CrmBooleanProperty>(() => attributeValue = ((CrmBooleanProperty)property).ToCrm2011BooleanAttributeValue()),
                          TypeSwitch.Case<DynamicEntityArrayProperty>(() => attributeValue = ((DynamicEntityArrayProperty)property).ToCrm2011EntityCollectionAttributeValue()),
                          TypeSwitch.Case<CustomerProperty>(() => attributeValue = property.ToCrm2011EntityReferenceAttributeValue()),
                          TypeSwitch.Case<OwnerProperty>(() => attributeValue = property.ToCrm2011EntityReferenceAttributeValue()),
                          TypeSwitch.Case<LookupProperty>(() => attributeValue = property.ToCrm2011EntityReferenceAttributeValue()),
                          TypeSwitch.Case<CrmDateTimeProperty>(() => attributeValue = ((CrmDateTimeProperty)property).ToCrm2011DateTimeAttributeValue()),
                          TypeSwitch.Case<CrmDecimalProperty>(() => attributeValue = ((CrmDecimalProperty)property).ToCrm2011DecimalAttributeValue()),
                          TypeSwitch.Case<CrmFloatProperty>(() => attributeValue = ((CrmFloatProperty)property).ToCrm2011DoubleAttributeValue()),
                          TypeSwitch.Case<CrmNumberProperty>(() => attributeValue = ((CrmNumberProperty)property).ToCrm2011IntegerAttributeValue()),
                          TypeSwitch.Case<CrmMoneyProperty>(() => attributeValue = ((CrmMoneyProperty)property).ToCrm2011MoneyAttributeValue()),
                          TypeSwitch.Case<PicklistProperty>(() => attributeValue = ((PicklistProperty)property).ToCrm2011OptionSetValueAttributeValue()),
                          TypeSwitch.Case<KeyProperty>(() => attributeValue = ((KeyProperty)property).ToCrm2011KeyAttributeValue()),
                          TypeSwitch.Case<UniqueIdentifierProperty>(() => attributeValue = ((UniqueIdentifierProperty)property).ToCrm2011GuidAttributeValue()),
                          TypeSwitch.Case<StatusProperty>(() => attributeValue = ((StatusProperty)property).ToCrm2011OptionSetValueAttributeValue()),
                          TypeSwitch.Case<StringProperty>(() => attributeValue = ((StringProperty)property).ToCrm2011StringAttributeValue()),
                          TypeSwitch.Default(() => attributeValue = attributeValue = ConvertToDefaultCrm2011AttributeValue(property))
                );
            return attributeValue;
        }
        
        private static Object ConvertToDefaultCrm2011AttributeValue(Property property)
        {
            StringProperty prop = (StringProperty) property;
            return prop.ToCrm2011StringAttributeValue();
        }

        public static Object ToCrm2011BooleanAttributeValue(this CrmBooleanProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return property.Value.Value;
        }

        public static Boolean IsNull(this CrmBooleanProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011EntityCollectionAttributeValue(this DynamicEntityArrayProperty property)
        {
            DynamicEntity[] dynamicEntities = property.Value;
            EntityCollection entityCollection = new EntityCollection();
            if (dynamicEntities != null && dynamicEntities.Length > 0)
            {
                entityCollection.EntityName = dynamicEntities[0].Name;
                foreach (DynamicEntity dynamicEntity in dynamicEntities)
                {
                    Entity entity = dynamicEntity.ToEntity();
                    entityCollection.Entities.Add(entity);
                }
            }
            return entityCollection;
        }

        public static Object ToCrm2011EntityReferenceAttributeValue(this Property property)
        {
            if (property is CustomerProperty)
            {
                CustomerProperty prop = (CustomerProperty)property;
                return prop.ToCrm2011EntityReferenceAttributeValue();
            }
            else if (property is OwnerProperty)
            {
                OwnerProperty prop = (OwnerProperty)property;
                return prop.ToCrm2011EntityReferenceAttributeValue();
            }
            else if (property is LookupProperty)
            {
                LookupProperty prop = (LookupProperty)property;
                return prop.ToCrm2011EntityReferenceAttributeValue();
            }
            String errorMessage = String.Format("The property '{0}' with type '{1}' does not have a supported CRM 2011 entity reference conversion.", property.Name ?? "NULL", property.GetType());
            throw new XrmLibraryException(errorMessage);
        }

        public static Object ToCrm2011EntityReferenceAttributeValue(this CustomerProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return new EntityReference(property.Value.name, property.Value.Value);
        }

        public static Boolean IsNull(this CustomerProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011EntityReferenceAttributeValue(this OwnerProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return new EntityReference(property.Value.name, property.Value.Value);
        }

        public static Boolean IsNull(this OwnerProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011EntityReferenceAttributeValue(this LookupProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return new EntityReference(property.Value.name, property.Value.Value);
        }

        public static Boolean IsNull(this LookupProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }


        public static Object ToCrm2011DateTimeAttributeValue(this CrmDateTimeProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return property.Value.UserTime;
        }

        public static Boolean IsNull(this CrmDateTimeProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011DecimalAttributeValue(this CrmDecimalProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return property.Value.Value;
        }

        public static Boolean IsNull(this CrmDecimalProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011DoubleAttributeValue(this CrmFloatProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return property.Value.Value;
        }

        public static Boolean IsNull(this CrmFloatProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011IntegerAttributeValue(this CrmNumberProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return property.Value.Value;
        }

        public static Boolean IsNull(this CrmNumberProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011StringAttributeValue(this StringProperty property)
        {
            return property.Value;
        }

        public static Object ToCrm2011MoneyAttributeValue(this CrmMoneyProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return new Money(property.Value.Value);
        }

        public static Boolean IsNull(this CrmMoneyProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011OptionSetValueAttributeValue(this PicklistProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return new OptionSetValue(property.Value.Value);
        }

        public static Boolean IsNull(this PicklistProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011KeyAttributeValue(this KeyProperty property)
        {
            return property.Value.Value;
        }

        public static Object ToCrm2011OptionSetValueAttributeValue(this StatusProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return new OptionSetValue(property.Value.Value);
        }

        public static Boolean IsNull(this StatusProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }

        public static Object ToCrm2011GuidAttributeValue(this UniqueIdentifierProperty property)
        {
            if (property.IsNull())
            {
                return null;
            }
            return property.Value.Value;
        }

        public static Boolean IsNull(this UniqueIdentifierProperty property)
        {
            return property.Value.IsNull && property.Value.IsNullSpecified;
        }
    }
}